package edu.purdue.cs.aggr.accumulators;

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import edu.purdue.cs.aggr.AbstractAccumulator;
import edu.purdue.cs.aggr.Accumulator;

public class MicrobenchAccumulator extends AbstractAccumulator implements Accumulator<Long, LinkedList<Long>> {


  public enum GrowthFactor { D_NEGONE, ONE, D_SQRT, D }

  //TODO: set this via commandline when the program starts
  public GrowthFactor whichtest;
  private LinkedList<Long> data = new LinkedList<Long>();

  public MicrobenchAccumulator() {
    super(null);
  }
  
  public MicrobenchAccumulator(String sourceId) {
    super(sourceId);
  }
  
  public MicrobenchAccumulator(Integer type, String sourceId) {
    super(sourceId);

	switch (type.intValue()) {
	case 0:
		whichtest = GrowthFactor.D_NEGONE;
		break;
	case 1:
		whichtest = GrowthFactor.ONE;
		break;
	case 2:
		whichtest = GrowthFactor.D_SQRT;
		break;
	case 3:
		whichtest = GrowthFactor.D;
		break;
	default:
		whichtest = GrowthFactor.D_NEGONE;
	}
  }
	
	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
	  super.writeExternal(out);
		out.writeObject(data);
		out.writeObject(whichtest);
	}

	@Override
	public void readExternal(ObjectInput in) throws IOException,
			ClassNotFoundException {
	  super.readExternal(in);
		data = (LinkedList<Long>) in.readObject();
		whichtest = (GrowthFactor) in.readObject();
	}

	@Override
	public void initialize(Long input) {
		data.add(input);
	}

	@Override
	public void addValue(Long input) {
		data.add(input);
	}

	@Override
	public LinkedList<Long> getAccumulation() {
	  return data;
	}

	@Override
	public void addAccumulators(List<Accumulator<Long, LinkedList<Long>>> accumulators) {
		System.out.println("Adding accumulators...");
		
		if(accumulators.size() == 0) {
			return;
		}
		
		Random rnd = new Random();
	    rnd.setSeed(System.currentTimeMillis());

	    long basesize = accumulators.get(0).getAccumulation().size();
	    long d = accumulators.size();
	    
	    if (data.size() > 0) {d++;}

	    long outputsize = getOutputSize(basesize, d);
	    
	    System.out.println("Generating output size of " + outputsize);

	    //You can change V to increase the time to aggregate
	    int V = 20;
	    for (long counter = 0; counter < d*V*basesize; counter++) {
	      data.push(rnd.nextLong());
	      if (data.size() > outputsize) {
	        data.pop();
	      }
	    }
	}
	
    private long getOutputSize(long base, long fanout) {
	    if (whichtest == GrowthFactor.D_NEGONE) {
	      System.out.println("whichtest: D_NEGONE");	
	      return base/fanout;
	    }
	    if (whichtest == GrowthFactor.ONE) {
	      System.out.println("whichtest: ONE");
	      return base;
	    }
	    if (whichtest == GrowthFactor.D_SQRT) {
	      System.out.println("whichtest: D_SQRT" + "\n" + base +"*" + Math.sqrt(fanout));
	      return Math.round(new Double(base) * Math.sqrt(fanout));
	    }
	    if (whichtest == GrowthFactor.D) {
	      System.out.println("whichtest: D");
	      return base * fanout;
	    }
	    System.out.println("whichtest: undefined");
	    return 0;
	  }
    
    public static void main(String[] args) {
        // Testing merge sort
        long[] valueArr = {70, 80, 100, 80, 50, 40, 98, 103, 20, 10, 99, 74};
        long[] supportArr = new long[valueArr.length];
        System.arraycopy(valueArr, 0, supportArr, 0, valueArr.length);
        
//        TopKMapper.K = 5;
        
        for (int i=0; i<valueArr.length; i++) {
        	System.out.print(valueArr[i] + ", ");
        }
    }
}
